<?php

class Buzz_DownloadProxy {
    const HTTP_TYPE = 0;
    const TORRENT_TYPE = 1;

    const GENERIC_HTTPSUBTYPE = 10;
    const IMAGE_HTTPSUBTYPE = 11;
    const LINK_HTTPSUBTYPE = 12;

    const GENERIC_TORRENTSUBTYPE = 20;

    private static $instance;

    /**
     *
     * @return Buzz_DownloadProxy
     */
    public static function instance() {
        if (!isset(self::$instance)) {
            self::$instance = new Buzz_DownloadProxy("../http-downloader/config.xml");
        }
        return self::$instance;
    }

    private $requestFolder;
    private $dataFolder;
    private $wkhtml;

    /**
     * Perform a download request that is handled by one of the downloaders.
     * @param int $type HTTP_TYPE or TORRENT_TYPE
     * @param string $url url (local or http) to element-generating file
     * @param int $subtype use SUBTYPES of this class. Useful for images.
     * @param int $id element id in database.
     * @return string Unique key that identifies this download.
     */
    public function requestDownload($type, $url, $subtype, $id) {
        if ($type == self::HTTP_TYPE) {
            if ($subtype == self::LINK_HTTPSUBTYPE) {
                return $img_id = $this->makeScreenshot($url);
            } else {
                $result = $this->createHttpDownloadRequest($url, $subtype);
                $this->updateDb($result, $id, $type);
                return $result;
            }
        } else {
            throw new Exception("Not yet implemented.");
        }
    }

    /**
     * Perform a download request that is handled by one of the downloaders.
     * <b>/!\</b> This method is solely used when the handled item is
     * an <b>IMAGE</b> and not an <b>ELEMENT</b>.
     * @param string $url url (local or http) to element-generating file
     * @param int $id element id in database.
     * @return string Unique key that identifies this download.
     */
    public function requestImageDownload($url, $id) {
        $result = $this->createHttpDownloadRequest($url, self::IMAGE_HTTPSUBTYPE);
        $this->updateImageInDb($result, $id);
        $json = $this->getJsonData($result);
        return $result;
    }

    public function getDestination($key, $type) {
        if ($type == self::HTTP_TYPE) {
            $json = $this->getJsonData($key);
            if ($json !== false) {
                return $json["destination"];
            } else {
                return null;
            }
        }
    }

    public function getState($key, $type) {
        if ($type == self::HTTP_TYPE) {
            $json = $this->getJsonData($key);
            if ($json !== false) {
                return $json["state"];
            } else {
                return "complete";
            }
        }
    }

    public function updateDb($key, $id, $type, $can_unlink = true) {
        if ($type == self::HTTP_TYPE) {
            $json = $this->getJsonData($key);
            $mapper = new Application_Model_ElementMapper();
            $elem = $mapper->find($id);
            $elem->set_tech_id($key);
            if ($json) {
                $elem->set_available_size($json["currentSize"]);
                $elem->set_total_size($json["totalSize"]);
                $elem->set_status($json["state"]);
            }
            $mapper->save($elem);
        }
        if ($can_unlink && $json["state"] == "complete") {
            $this->destroy($key, $type);
        }

        if (isset($elem)) {
            return $elem;
        } else {
            return null;
        }
    }

    private function updateImageInDb($key, $id) {
        $json = $this->getJsonData($key);
        $mapper = new Application_Model_ImageMapper();
        $image = $mapper->find($id);
        if ($image) {
            $image->set_uri('/images/' . $json["destination"]);
            $mapper->save($image);
        }

        if ($json['state'] == 'complete') {
            $this->destroy($key, self::HTTP_TYPE);
        }
    }

    private function saveNewImage($key, $ext) {
        $img = new Application_Model_Image(null, '/images/images/' . $key . $ext);
        $mapper = new Application_Model_ImageMapper();
        return $mapper->save($img);
    }

    private function makeScreenshot($url) {
        $key = $this->generateUniqueFilename();
        $out = 0;
        exec($this->wkhtml . ' --crop-h 500 --crop-w 500 ' . $url . ' ' . $this->dataFolder . '/images/' . $key . '.jpg 2>&1');
        return $this->saveNewImage($key, ".jpg");
    }

    private function getJsonData($key) {
        if (file_exists($this->requestFolder . "/" . $key . ".json")) {
            return json_decode(file_get_contents($this->requestFolder . "/" . $key . ".json"), true);
        } else {
            return null;
        }
    }

    private function createHttpDownloadRequest($url, $subtype) {
        $json = array("currentSize" => 0, "totalSize" => 0, "url" => $url,
            "state" => "waiting");
        $unique = $this->generateUniqueFilename();
        $json["destination"] = $unique . $this->getExtension($url);
        if ($subtype == self::IMAGE_HTTPSUBTYPE) {
            $json["destination"] = "images/" . $json["destination"];
        }
        file_put_contents($this->requestFolder . "/" . $unique . ".json", json_encode($json));
        chmod($this->requestFolder . "/" . $unique . ".json", 0777);
        return $unique;
    }

    private function getExtension($url) {
        return substr($url, strrpos($url, "."));
    }

    private function destroy($key, $type) {
        if ($type == self::HTTP_TYPE) {
            unlink($this->requestFolder . "/" . $unique . ".json");
        }
    }

    private function  __construct($httpdlconfig) {
        $sxml = simplexml_load_file($httpdlconfig);
        $this->requestFolder = $sxml->monitoredFolder;
        $this->dataFolder = $sxml->destinationFolder;
        $ini = new Zend_Config_Ini(APPLICATION_PATH . "/configs/application.ini", "production");
        $this->wkhtml = $ini->binaries->wkhtml;
    }

    private function generateUniqueFilename() {
        $str = "";
        for ($i = 0; $i < 42; $i++) {
            $type = rand(0, 2);
            if ($type == 0) {
                $str .= chr(rand(48, 57));
            } elseif ($type == 1) {
                $str .= chr(rand(65, 90));
            } else {
                $str .= chr(rand(97, 122));
            }
        }
        return $str;
    }
}
